package com.apobates.forum.core.impl.service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.apobates.forum.core.api.ImageIOMeta;
import com.apobates.forum.core.api.dao.PostsDao;
import com.apobates.forum.core.api.service.PostsService;
import com.apobates.forum.core.entity.Posts;
import com.apobates.forum.core.impl.event.ForumEventPublisher;
import com.apobates.forum.core.impl.event.PostsPublishEvent;
import com.apobates.forum.decorater.ForumEncoder;
import com.apobates.forum.decorater.Posts.ForumPostsDecorator;
import com.apobates.forum.event.elderly.ActionDescriptor;
import com.apobates.forum.event.elderly.ActionEventCulpritor;
import com.apobates.forum.event.elderly.ForumActionEnum;
import com.apobates.forum.member.api.service.MemberService;
import com.apobates.forum.member.entity.Member;
import com.apobates.forum.utils.DateTimeUtils;
import com.apobates.forum.utils.persistence.Page;
import com.apobates.forum.utils.persistence.Pageable;

@Service
public class PostsServiceImpl implements PostsService{
	@Autowired
	private PostsDao postsDao;
	@Autowired
	private ForumEventPublisher forumEventPublisher;
	@Autowired
	private MemberService memberService;
	private final static Logger logger = LoggerFactory.getLogger(PostsServiceImpl.class);
	
	@ActionDescriptor(action=ForumActionEnum.POSTS_REPLY, isRedress=false)
	@Override
	public long create(int volumesId, long boardId, long topicId, String content, ImageIOMeta imageIO, ActionEventCulpritor culpritor)throws IllegalStateException {
		//编码回复内容
		String encodePostsContent = new ForumEncoder(content)
				.encodeSmile(imageIO.getImageBucketDomain(), imageIO.getSmileyDirectName())
				.encodeUploadImage(imageIO.getImageBucketDomain(), imageIO.getUploadImageDirectName())
				.relaxedHtmlTag()
				.parseEmoji()
				.getContent();
		//-------------------------------------------------------------------------
		//加工内容|楼层交由事务去填充
		Posts posts = new Posts(encodePostsContent, culpritor.getMemberId(), culpritor.getMemberNickname(), topicId, volumesId, boardId, -1, culpritor.getIpAddr());
		postsDao.save(posts);
		if(posts.getId()<1) {
			throw new IllegalStateException("话题回复创建失败");
		}
		
		forumEventPublisher.publishPostsEvent(new PostsPublishEvent(this, posts, culpritor.getUserAgent()));
		//
		return posts.getId();
	}

	@ActionDescriptor(action=ForumActionEnum.POSTS_EDIT)
	@Override
	public Optional<Boolean> edit(long id, String content, ImageIOMeta imageIO, ActionEventCulpritor culpritor)throws IllegalStateException {
		//编码回复内容
		String encodePostsContent = new ForumEncoder(content)
				.encodeSmile(imageIO.getImageBucketDomain(), imageIO.getSmileyDirectName())
				.encodeUploadImage(imageIO.getImageBucketDomain(), imageIO.getUploadImageDirectName())
				.relaxedHtmlTag()
				.parseEmoji()
				.getContent();
		//-------------------------------------------------------------------------
		//加工内容
		return postsDao.edit(id, encodePostsContent, culpritor.getMemberId(), culpritor.getMemberNickname());
	}
	
	@ActionDescriptor(action=ForumActionEnum.POSTS_DEL)
	@Override
	public Optional<Boolean> remove(long id, ActionEventCulpritor culpritor) {
		return postsDao.editStatus(id, false);
	}

	@Override
	public Optional<Posts> get(long id) { //需要缓存吗?被引用的频率?被编辑的频率?
		if(id<=0){
			return Optional.empty();
		}
		return postsDao.findOne(id);
	}

	@Override
	public Optional<Posts> getPostsContentForEdit(long id, final ImageIOMeta imageIO) {
		if(id<=0){
			return Optional.empty();
		}
		Consumer<Posts> action = p->{
			String postsBody = ForumPostsDecorator.init(p)
							.decorateSmileImage(imageIO.getImageBucketDomain(), imageIO.getSmileyDirectName())
							.decorateUploadImage(imageIO.getImageBucketDomain(), imageIO.getUploadImageDirectName(), false, null)
							.getContent();
			p.setContent(postsBody);
		};
		return decorateContent(()->postsDao.findOne(id), action);
	}

	@Override
	public Optional<Posts> getPostsContentForEditByTopic(long topicId, final ImageIOMeta imageIO) {
		if(topicId<=0){
			return Optional.empty(); //.failure("参数不合法或不被接受");
		}
		Consumer<Posts> action = p->{
			String postsBody = ForumPostsDecorator.init(p)
							.decorateSmileImage(imageIO.getImageBucketDomain(), imageIO.getSmileyDirectName())
							.decorateUploadImage(imageIO.getImageBucketDomain(), imageIO.getUploadImageDirectName(), false, null)
							.getContent();
			p.setContent(postsBody);
		};
		return decorateContent(()->getTopicContent(topicId), action);
	}
	
	@Override
	public Optional<Posts> getPostsContentForQuote(long id, final ImageIOMeta imageIO, final boolean lazyLoad, final String imageScale, final String dictionaryFilePath) {
		if(id<=0){
			return Optional.empty(); //.failure("参数不合法或不被接受");
		}
		Consumer<Posts> action = p->{
			String postsBody = ForumPostsDecorator.init(p)
							.block("作者被禁止发言或内容自动屏蔽")
							.sensitiveWord(dictionaryFilePath)
							.decorateSmileImage(imageIO.getImageBucketDomain(), imageIO.getSmileyDirectName())
							.decorateUploadImage(imageIO.getImageBucketDomain(), imageIO.getUploadImageDirectName(), lazyLoad, imageScale)
							.modify("回复最近由管理人员进行过编辑")
							.getContent();
			p.setContent(postsBody);
		};
		return decorateContent(()->postsDao.findOne(id), action);
	}

	@Override
	public Optional<Posts> getTopicContent(long topicId) {
		return postsDao.findOneByOneFloor(topicId);
	}

	@Override
	public Page<Posts> getAll(long topicId, Pageable pageable) {
		Page<Posts> rs = postsDao.findAllByTopic(topicId, pageable);
		return copyPosts(rs, null);
	}

	@Override
	public Posts get(long id, long topicId) {
		return postsDao.findOneRelativeTopic(id);
	}

	@Override
	public Page<Posts> getAllReply(long topicId, Pageable pageable) {
		return postsDao.findAllReplyByTopic(topicId, pageable);
	}
	//202001162205
	@Override
	public Page<Posts> getAllReply(long topicId, Pageable pageable, ImageIOMeta imageIO, boolean lazyLoad, String imageScale, String dictionaryFilePath) {
		Page<Posts> rs = postsDao.findAllReplyByTopic(topicId, pageable);
		Consumer<Posts> pc = (Posts pr) -> {
			// 解码图片连接编码
			// 解码图片连接编码
			String postsBody = ForumPostsDecorator
					.init(pr)
					.block("*")
					.sensitiveWord(dictionaryFilePath)
					.decorateSmileImage(imageIO.getImageBucketDomain(), imageIO.getSmileyDirectName())
					.decorateUploadImage(imageIO.getImageBucketDomain(), imageIO.getUploadImageDirectName(), lazyLoad, imageScale)
					.getContent();
			pr.setContent(postsBody);
		};
		return copyPosts(rs, pc);
	}
	//202001162205
	@Override
	public Page<Posts> getAllReplyFilterAuthor(long topicId, long filterMemeberId, Pageable pageable, ImageIOMeta imageIO, boolean lazyLoad, String imageScale, String dictionaryFilePath) {
		if (filterMemeberId < 1) {
			return getAllReply(topicId, pageable, imageIO, lazyLoad, imageScale, dictionaryFilePath);
		}
		Page<Posts> rs = postsDao.findAllReplyByTopic(topicId, filterMemeberId, pageable);
		Consumer<Posts> pc = (Posts pr) -> {
			// 解码图片连接编码
			// 解码图片连接编码
			String postsBody = ForumPostsDecorator
					.init(pr)
					.block("*")
					.sensitiveWord(dictionaryFilePath)
					.decorateSmileImage(imageIO.getImageBucketDomain(), imageIO.getSmileyDirectName())
					.decorateUploadImage(imageIO.getImageBucketDomain(), imageIO.getUploadImageDirectName(), lazyLoad, imageScale)
					.getContent();
			pr.setContent(postsBody);
		};
		return copyPosts(rs, pc);
	}

	@Override
	public Posts get(long id, long topicId, long boardId, int boardGroupId) {
		return postsDao.findOneForIndex(id);
	}

	@Override
	public Stream<Posts> getRecentByUnixStamp(long topicId, int prevUnixStamp) {
		LocalDateTime prevDate = DateTimeUtils.getDateTimeByUnixTimestamp(prevUnixStamp);
		return postsDao.findAllRecentByTopic(topicId, prevDate);
	}

	@Override
	public Stream<Posts> getRecentForTopicIgnoreStatus(long topicId, int size) {
		return postsDao.findAllRecentByTopicIgnoreStatus(topicId, size);
	}

	@Override
	public Stream<Posts> getTopicLordContent(List<Long> topicIdList) {
		if(topicIdList == null || topicIdList.isEmpty()){
			return Stream.empty();
		}
		return postsDao.findOneByOneFloor(topicIdList);
	}

	@Override
	public Stream<Posts> getRecentForRSS(long topicId, int size, ImageIOMeta imageIO, boolean lazyLoad, String dictionaryFilePath) {
		Consumer<Posts> action = posts->{
			//解码图片连接编码
			String postsBody = ForumPostsDecorator.init(posts)
					.block("作者被禁止发言或内容自动屏蔽")
					.sensitiveWord(dictionaryFilePath)
					.decorateSmileImage(imageIO.getImageBucketDomain(), imageIO.getSmileyDirectName())
					.decorateUploadImage(imageIO.getImageBucketDomain(), imageIO.getUploadImageDirectName(), lazyLoad, null)
					.modify("回复最近由管理人员进行过编辑")
					.getContent();
			posts.setContent(postsBody);
		};
		return postsDao.findAllByTopic(topicId, size).peek(action);
	}

	@Override
	public long maxFloor(long topicId) {
		return postsDao.maxFloor(topicId);
	}

	@Override
	public long countRepliesSize(long topicId, long replyer) {
		return postsDao.countRepliesSize(topicId, replyer);
	}

	@Override
	public long countRepliesSize(long topicId) {
		return postsDao.countTopicReply(topicId);
	}

	@Override
	public long getTopicPageSize(long topicId, int pageSize) {
		long mf = maxFloor(topicId);
		if(mf <= pageSize){
			return 1;
		}
		long page = mf / pageSize;
		if(mf % pageSize != 0){
			page += 1;
		}
		return page;
	}

	private Page<Posts> copyPosts(Page<Posts> rs, final Consumer<Posts> postsConsumer) {
		List<Posts> posts = rs.getResult().collect(Collectors.toList());
		if (null == posts || posts.isEmpty()) {
			return Page.empty();
		}
		// AVOID require memberService
		List<Long> memberIdList = posts.stream().map(Posts::getMemberId).collect(Collectors.toList());
		Map<Long, Member> memberSet = memberService.queryCollection(memberIdList).collect(Collectors.toMap(Member::getId, Function.identity()));
		BiFunction<Posts, Member, Posts> bi = (p, m) -> {
			Optional<Posts> pr = Optional.ofNullable(p);
			pr.ifPresent(pc -> {
				Member safeIns = new Member();
				safeIns.setId(m.getId());
				safeIns.setNickname(m.getNickname());
				safeIns.setSignature(m.getSignature());
				safeIns.setAvatarURI(m.getAvatarURI());
				safeIns.setRegisteDateTime(m.getRegisteDateTime());
				safeIns.setMgroup(m.getMgroup());
				safeIns.setMrole(m.getMrole());
				safeIns.setStatus(m.getStatus());
				//
				pc.setMember(safeIns);
				if (null != postsConsumer) {
					postsConsumer.accept(pc);
				}
			});
			return pr.orElse(null);
		};
		Stream<Posts> result = posts.parallelStream().map(pobj -> bi.apply(pobj, memberSet.getOrDefault(pobj.getMemberId(), Member.guestMember()))).filter(Objects::nonNull);
		return new Page<Posts>() {
			@Override
			public long getTotalElements() {
				return rs.getTotalElements();
			}

			@Override
			public Stream<Posts> getResult() {
				return result;
			}
		};
	}
	
	private Optional<Posts> decorateContent(Supplier<Optional<Posts>> srcTarget, Consumer<Posts> action){
		Optional<Posts> opt = srcTarget.get();
		opt.ifPresent(action);
		return opt;
	}
}
